package cn.demoncat.util.pay.cmb;

import cn.demoncat.util.exception.BizErrorRuntimeException;
import cn.demoncat.util.exception.BizRuntimeException;
import cn.demoncat.util.lang.JsonUtil;
import cn.demoncat.util.lang.LocalDateUtil;
import cn.demoncat.util.lang.NumberUtil;
import cn.demoncat.util.lang.constant.DateConstant;
import cn.demoncat.util.lang.entity.ListObj;
import cn.demoncat.util.lang.entity.MapSo;
import cn.demoncat.util.lang.entity.MapSs;
import cn.demoncat.util.pay.cmb.config.CmbProperties;
import cn.demoncat.util.pay.cmb.constant.CmbApi;
import cn.demoncat.util.pay.cmb.constant.CmbLogs;
import cn.demoncat.util.pay.cmb.entity.CmbAccountInfoResult;
import cn.demoncat.util.pay.cmb.entity.CmbLogResult;
import cn.demoncat.util.pay.cmb.entity.CmbParam;
import cn.demoncat.util.pay.cmb.entity.CmbResult;
import cn.demoncat.util.pay.cmb.util.DcHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.security.Security;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 招商银行银企直连
 *
 * @author 延晓磊
 * @since 2024年10月31日
 */
@Slf4j
@Component
public class CmbUtil {

    @Resource
    private CmbProperties cmbProperties;

    static {
        // 装载BC库
        Security.addProvider(new BouncyCastleProvider());
        System.setProperty("sun.net.http.retryPost", "false");
    }

    /**
     * 查询可经办业务模式
     *
     * @param biz   业务代码：N02030 支付
     * @return
     */
    public String getMod(String biz){
        return this.request(CmbApi.Mod, MapSs.getInstance("buscod", biz));
    }

    /**
     * 查询账户信息
     *
     * @return
     */
    public CmbAccountInfoResult getAccountInfo(){
        String response = this.request(CmbApi.AccountInfo, MapSo.getInstance("ntqacinfx", ListObj.getInstance(MapSs.getInstance("accnbr", cmbProperties.getBankNo()))));
        return JsonUtil.parseObject(response).getJSONArray("ntqacinfz").getJSONObject(0).toJavaObject(CmbAccountInfoResult.class);
    }

    /**
     * 查询流水
     *
     * @param beginDate 开始日期（包含）
     * @param endDate   结束日期（包含，默认beginDate）
     * @return 流水列表
     */
    public List<CmbLogResult.Z2> log(LocalDate beginDate, LocalDate endDate) {
        // 时间
        String start = LocalDateUtil.format(beginDate, DateConstant.DATE_FORMAT_NUM);
        String end = endDate == null ? start : LocalDateUtil.format(endDate, DateConstant.DATE_FORMAT_NUM);
        // 首查
        CmbLogResult result = this.log(start, end, null, null);
        List<CmbLogResult.Z2> logs = new ArrayList<>();
        if (CollectionUtils.isEmpty(result.getTRANSQUERYBYBREAKPOINT_Z2())) {
            return logs;
        }
        logs.addAll(result.getTRANSQUERYBYBREAKPOINT_Z2());
        while (result.getTRANSQUERYBYBREAKPOINT_Z1().get(0).hasMore()) {
            // 续查
            result = this.log(start, end, result.getTRANSQUERYBYBREAKPOINT_Z1().get(0).getQueryAcctNbr(), result.getTRANSQUERYBYBREAKPOINT_Y1());
            logs.addAll(result.getTRANSQUERYBYBREAKPOINT_Z2());
        }
        return logs;
    }

    /**
     * 支付
     *
     * @param crtAcc    收款账户
     * @param crtNam    收款户主
     * @param crtBnk    收款行名称
     * @param brdNbr    收款联行号（可空）
     * @param trsAmt    金额
     * @param nusAge    用途（银行摘要 remarkTextClt）
     * @param busNar    备注（业务回传，可空 businessText）
     * @param yurRef    业务唯一编码（防止重复提交）
     */
    public void pay(String crtAcc, String crtNam, String crtBnk, String brdNbr, BigDecimal trsAmt, String nusAge, String busNar, String yurRef){
        this.request(CmbApi.Pay, MapSo.getInstance(
                // 模式：自动
                "bb1paybmx1", ListObj.getInstance(MapSs.getInstance(
                        "busMod", cmbProperties.getPayMod(), "busCod", "N02030"
                )),
                // 支付信息
                "bb1payopx1", ListObj.getInstance(MapSs.getInstance(
                        "dbtAcc", cmbProperties.getBankNo(), "crtAcc", crtAcc, "crtNam", crtNam, "crtBnk", crtBnk, "brdNbr", brdNbr, "ccyNbr", "10", "trsAmt", NumberUtil.format(trsAmt, NumberUtil.Format.PRICE),
                        "bnkFlg", crtBnk.contains("招商") ? "Y" : "N", "stlChn", "I", "nusAge", nusAge, "yurRef", yurRef, "busNar", busNar, "trsTyp", "100001"
                ))
        ));
    }

    /**
     * 发起请求
     *
     * @param api       接口
     * @param data      接口参数
     * @return
     */
    private String request(CmbApi api, Object data){
        // 请求
        log.info(CmbLogs.REQUEST, api.getName());
        String param = CmbParam.build(api.getCode(), cmbProperties.getUid(), data);
        log.debug(CmbLogs.REQUEST_PARAM, api.getName(), param);
        String response;
        try {
            response = new DcHelper(cmbProperties.getUrl(), cmbProperties.getUid(), cmbProperties.getPrivateKey(), cmbProperties.getPublicKey(), cmbProperties.getSmKey()).sendRequest(param, api.getCode());
        } catch (Exception e) {
            log.error(CmbLogs.REQUEST_FAIL, e.getMessage(), e);
            throw new BizErrorRuntimeException("请求招商银行失败：" + e.getMessage(), e);
        }
        // 解析
        log.debug(CmbLogs.REQUEST_RESULT, api.getName(), response);
        CmbResult result = JsonUtil.parse(response, CmbResult.class);
        if (Objects.requireNonNull(result).getResponse().getHead().getResultcode().equals("SUC0000")) {
            return result.getResponse().getBody();
        }else{
            log.warn(CmbLogs.REQUEST_ERROR, api.getName(), result.getResponse().getHead().getResultcode(), result.getResponse().getHead().getResultmsg());
            throw new BizRuntimeException("请求招商银行出错：" + result.getResponse().getHead().getResultmsg());
        }
    }

    /**
     * 查询流水
     *
     * @param start 起始日期
     * @param end   结束日期
     * @param queryAcctNbr  继续查询号
     * @return
     */
    private CmbLogResult log(String start, String end, String queryAcctNbr, List<CmbLogResult.Y1> y1){
        Map<String,Object> param = MapSo.getInstance(
                "TRANSQUERYBYBREAKPOINT_X1", ListObj.getInstance(MapSs.getInstance(
                        "cardNbr", cmbProperties.getBankNo(), "beginDate", start, "endDate", end, "queryAcctNbr", queryAcctNbr
                )));
        if (StringUtils.isNotBlank(queryAcctNbr)) {
            // 续查
            param.put("TRANSQUERYBYBREAKPOINT_Y1", y1);
        }
        return JsonUtil.parse(this.request(CmbApi.LOG, param), CmbLogResult.class);
    }

}
